package org.springframework.beans.factory.annotation;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.TypeUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.core.convert.ConversionService;

import java.lang.reflect.Field;

/**
 * 处理@Autowired和@Value注解的BeanPostProcessor
 * 也就是对bean的属性赋值做处理的处理器
 */
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }

    /**
     * 该方法是在实例化bean之后 但是给属性赋值之前执行 所以这里对属性的值进行了加工处理
     *
     * @param pvs
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        //处理@Value注解
        //其实本质上就是通过反射 给bean定义对象里的属性值列表的属性赋值而已
        Class<?> clazz = bean.getClass();
        Field[] fields = clazz.getDeclaredFields();
        //遍历当前bean的所有字段
        for (Field field : fields) {
            //取出字段的value注解 如果有则解析后赋值到bean定义对象李 如果没有则不处理
            Value valueAnnotation = field.getAnnotation(Value.class);
            if (valueAnnotation != null) {
                Object value = valueAnnotation.value();
                //解析占位符
                value = beanFactory.resolveEmbeddedValue((String) value);

                //执行类型转换
                //获取value的类型
                Class<?> sourceType = value.getClass();

                //获取bean里这个属性的真实的类型
                Class<?> targetType = (Class<?>) TypeUtil.getType(field);

                //转换为真实的目标类型
                ConversionService conversionService = beanFactory.getConversionService();
                if (conversionService != null){
                    //可以转换就转换
                    if(conversionService.canConvert(sourceType, targetType)){
                        value = conversionService.convert(value, targetType);
                    }
                }

                //这里已经把赋值的属性 以及类型转换后的属性
                //添加到bean里了 可是最终返回属性值pvs 还返回干哈
                BeanUtil.setFieldValue(bean, field.getName(), value);
            }
        }

        //处理@Autowired注解
        for (Field field : fields) {
            //遍历每个字段 取出autowired注解
            Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
            if(autowiredAnnotation != null){
                //获取字段的类型 也就是bean的类型
                Class<?> fieldType = field.getType();

                //创建变量 是用来保存 要依赖的bean的名称的
                String dependentBeanName = null;
                Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
                Object dependentBean = null;

                if(qualifierAnnotation != null){
                    //Qualifier注解不为空时 根据类型和名字同时获取bean 否则只根据类型获取
                    dependentBeanName = qualifierAnnotation.value();
                    dependentBean = beanFactory.getBean(dependentBeanName, fieldType);
                }else {
                    dependentBean = beanFactory.getBean(fieldType);
                }

                //最后将依赖的bean属性赋值上
                BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
            }
        }


        //TODO 这个pvs实际上是没有改变过的啊。。
        return pvs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

}
